#include <jni.h>
#include <string>

#include <cmath>
#include <cstdio>
#include <cstdlib>

#include <sys/time.h>


#include "nativlib.h"
#include "log.h"

int64_t getCurrentMillTime() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return ((int64_t) tv.tv_sec * 1000 + (int64_t) tv.tv_usec / 1000);//毫秒
}


#define FEATURE_COUNT 5
extern "C" JNIEXPORT jstring

JNICALL stringFromJNI(JNIEnv *env, jclass clazz, jint number) {
    std::string result = "0";
    std::string name_faiss = "/sdcard/tmp/1.index";
//  faiss::IndexFlatL2 *index;


    index = new faiss::IndexFlatL2(FEATURE_COUNT);
    int vec_nums = 5;
    float datas[vec_nums][FEATURE_COUNT];
    for (int i = 0;i < vec_nums;i++)
    {
        for (int j = 0;j<FEATURE_COUNT;j++)
        {
            datas[i][j] = 0.0001f * FEATURE_COUNT * 2 + 0.00002f+0.000002f*(105-i);
        }
    }


    float data[FEATURE_COUNT];//random data

    for (int i = 0; i < FEATURE_COUNT; i++) {
        data[i] = 0.0001f * FEATURE_COUNT * 2 + 0.00002f;
    }
    LOGI("index->add。。。");
    for (int i = 0; i < (vec_nums); i++) {

        index->add(1, datas[i]);
        //Problems may occur in 32-bit

        LOGD("SIZE=%d  %lld",index->ntotal, index->ntotal * index->d);
    }
    LOGI("index->add over");


    LOGI("save...");
    //please apply for sdcard permission before writing
    faiss::write_index(index, name_faiss.c_str());
    LOGI("save ");
    delete index;


    LOGI("read ... ");
    faiss::Index *tmp = faiss::read_index(name_faiss.c_str(), faiss::IO_FLAG_MMAP);
    LOGI("read ok");
    //null point check
    index = (faiss::IndexFlatL2 *) tmp;

    LOGI("index->d=%d", index->d);


    float read[FEATURE_COUNT] = {0};
    index->reconstruct(index->ntotal - 1, read);

    for (int i = 0; i < FEATURE_COUNT; i++) {
        LOGI("read[%d] %f %f", i, data[i], read[i]);
    }

    float data2[FEATURE_COUNT];//random data
    for (int i = 0; i < FEATURE_COUNT; i++) {
        data2[i] = 0.0001f * FEATURE_COUNT * 2 + 0.00002f;
    }
    const int64_t destCount = 3;
    int64_t *listIndex = (int64_t *) malloc(sizeof(int64_t) * destCount);
    float *listScore = (float *) malloc(sizeof(float) * destCount);
    LOGI("index->search。。。");
    index->search(1, data2, destCount, listScore, listIndex);
    LOGI("index->search");
    for (int i = 0; i < destCount; i++) {
        LOGI("index->search[%lld]=%f", listIndex[i], listScore[i]);
    }

    free(listIndex);
    free(listScore);

    LOGI("read");
    result = std::to_string(listIndex[0]);
    return env->NewStringUTF(result.c_str());
}

//#define JNIREG_CLASS_BASE "com/brandservice/MainActivity"
//#define JNIREG_CLASS_BASE "io/datamachines/faiss/MainActivity"
#define JNIREG_CLASS_BASE "com/brandservice/utils/SearchUtil"
static JNINativeMethod gMethods_Base[] = {
        {"stringFromJNI", "(I)Ljava/lang/String;", (void *) stringFromJNI},
};

static int registerNativeMethods(JNIEnv *env, const char *className,
                                 JNINativeMethod *gMethods, int numMethods) {
    jclass clazz;
    clazz = (*env).FindClass(className);
    if (clazz == nullptr) {
        return JNI_FALSE;
    }
    if ((*env).RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}


static int registerNatives(JNIEnv *env) {
    if (!registerNativeMethods(env, JNIREG_CLASS_BASE, gMethods_Base,
                               sizeof(gMethods_Base) / sizeof(gMethods_Base[0]))) {
        return JNI_FALSE;
    }


    return JNI_TRUE;
}


JNIEXPORT jint

JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOGI("JNI_OnLoad");
    JNIEnv *env = nullptr;
    jint result = -1;

    if ((*vm).GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != nullptr);

    if (!registerNatives(env)) { //注册
        return -1;
    }

    result = JNI_VERSION_1_4;
    return result;

}


JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {


}



extern "C"
JNIEXPORT jstring
JNICALL Java_io_datamachines_faiss_MainActivity_test(JNIEnv *env, jclass clazz) {
    // TODO: implement test()

    const std::string &result = "wusiyu";
    return env->NewStringUTF(result.c_str());
}



//extern "C"
//JNIEXPORT jlong JNICALL
//Java_com_brandservice_utils_IndexUtil_addData(JNIEnv *env, jclass clazz, jfloatArray data_vector) {
//    // TODO: implement addData()
//
//    //这里涉及一个jfloatArray和一个float*转换的问题
//    if (index == nullptr)
//
//    index = new faiss::IndexFlatL2(FEATURE_COUNT);
//   jfloat* inarr  = env->GetFloatArrayElements(data_vector,NULL);
//
//   index->add(1, inarr);
//    return (index->ntotal);
//}
//extern "C"
//JNIEXPORT jint JNICALL
//Java_io_datamachines_faiss_IndexUtil_indexs_1search(JNIEnv *env, jclass clazz,
//                                                    jfloatArray query_vector, jint k,
//                                                    jfloatArray distance, jintArray indexs) {
//
//   jfloat *qv = env->GetFloatArrayElements(query_vector,NULL);
//
//    LOGI("query——vec");
//    for(int i = 0 ; i < 20;i++)
//        LOGI("abc%f",qv[i]);
//   jfloat *ds = env->GetFloatArrayElements(distance, NULL);
//   jint *ii = env->GetIntArrayElements(indexs,JNI_FALSE);
//    (*env).SetFloatArrayRegion(distance,0,k,ds);
//   //index->search(1, qv, k,
////                  ds, reinterpret_cast<faiss::Index::idx_t *>(ii));
//    const int64_t destCount = k;
//    int64_t *listIndex = (int64_t *) malloc(sizeof(int64_t) * destCount);
//    float *listScore = (float *) malloc(sizeof(float) * destCount);
//
//
//
//    LOGI("index->search。。。");
//    float read[FEATURE_COUNT] = {0};
//
//    index->search(1, qv
//                  , k, listScore, listIndex);
//    LOGI("index->search");
//
//    for (int i = 0; i < k; i++) {
////        LOGI("1111wusiyuindex->search[%d]=%f", listIndex[i], listScore[i]);
//ds[i] = listScore[i];
//ii[i] = listIndex[i];
//    }
//    (*env).SetFloatArrayRegion(distance,0,k,ds);
//    (*env).SetIntArrayRegion(indexs,0,k,ii);
//    free(listIndex);
//    free(listScore);
//
//    return index->ntotal;
//}
////extern "C"
////JNIEXPORT jobject JNICALL
////Java_io_datamachines_faiss_Get_getUser(JNIEnv *env, jclass clazz) {
////    // TODO: implement getUser()
////    jclass list = env->FindClass("java/util/ArrayList");
////    if (list == NULL) {
////    LOGI("aRRAY没有找到");
////    return nullptr;
////}
////    jmethodID list_init = env->GetMethodID(list,"<init>","()V");
////    jobject list_obj = env->NewObject(list,list_init);
////    jmethodID list_add = env->GetMethodID(list,"add","(Ljava/lang/Object;)Z");
////    jclass jcls = env->FindClass("pojo/User");
////    jfieldID names = env->GetFieldID(jcls,"name", "Ljava/lang/String;");
////    jfieldID ids = env->GetFieldID(jcls,"id", "I");
////    if (jcls == NULL)
////    {
////        LOGI("User没有找到");
////        return 0;
////    }
////    for (int i = 0 ; i < 4;i++)
////    {
////        jobject jobj = env->AllocObject(jcls);
////        char* name = new char[5];
////        sprintf(name,"0000%d",i);
////        int id = i;
////
////        env->SetIntField(jobj,ids,id);
////        env->SetObjectField(jobj,names,env->NewStringUTF(name));
////        env->CallBooleanMethod(list_obj,list_add,jobj);
////    }
////    return list_obj;
////
////}
////extern "C"
////JNIEXPORT jint JNICALL
////Java_com_brandservice_utils_IndexUtil_indexs_search(JNIEnv *env, jclass clazz,
////
////                                                     jfloatArray distance, jintArray indexs) {
////
////    jfloat *qv = env->GetFloatArrayElements(query_vector,NULL);
////
////     LOGI("query——vec");
////     for(int i = 0 ; i < 20;i++)
////         LOGI("abc%f",qv[i]);
////    jfloat *ds = env->GetFloatArrayElements(distance, NULL);
////    jint *ii = env->GetIntArrayElements(indexs,JNI_FALSE);
////     (*env).SetFloatArrayRegion(distance,0,k,ds);
////    //index->search(1, qv, k,
//// //                  ds, reinterpret_cast<faiss::Index::idx_t *>(ii));
////     const int64_t destCount = k;
////     int64_t *listIndex = (int64_t *) malloc(sizeof(int64_t) * destCount);
////     float *listScore = (float *) malloc(sizeof(float) * destCount);
////
////
////
////     LOGI("index->search。。。");
////     float read[FEATURE_COUNT] = {0};
////
////     index->search(1, qv
////                   , k, listScore, listIndex);
////     LOGI("index->search");
////
////     for (int i = 0; i < k; i++) {
//// //        LOGI("1111wusiyuindex->search[%d]=%f", listIndex[i], listScore[i]);
//// ds[i] = listScore[i];
//// ii[i] = listIndex[i];
////     }
////     (*env).SetFloatArrayRegion(distance,0,k,ds);
////     (*env).SetIntArrayRegion(indexs,0,k,ii);
////     free(listIndex);
////     free(listScore);
////
////     return index->ntotal;
//// }
extern "C"
JNIEXPORT jint JNICALL
Java_com_brandservice_utils_IndexUtil_indexs_1search(JNIEnv *env, jclass clazz,
                                                     jfloatArray query_vec, jint k,
                                                     jfloatArray diss, jintArray indexs) {
    jfloat *qv = env->GetFloatArrayElements(query_vec,NULL);

    LOGI("query——vec");
    for(int i = 0 ; i < 20;i++)
        LOGI("abc%f",qv[i]);
    jfloat *ds = env->GetFloatArrayElements(diss, NULL);
    jint *ii = env->GetIntArrayElements(indexs,JNI_FALSE);
    (*env).SetFloatArrayRegion(diss,0,k,ds);
    //index->search(1, qv, k,
    //                  ds, reinterpret_cast<faiss::Index::idx_t *>(ii));
    const int64_t destCount = k;
    int64_t *listIndex = (int64_t *) malloc(sizeof(int64_t) * destCount);
    float *listScore = (float *) malloc(sizeof(float) * destCount);



    LOGI("index->search。。。");
    float read[FEATURE_COUNT] = {0};

    index->search(1, qv
            , k, listScore, listIndex);
    LOGI("index->search");

    for (int i = 0; i < k; i++) {
        //        LOGI("1111wusiyuindex->search[%d]=%f", listIndex[i], listScore[i]);
        ds[i] = listScore[i];
        ii[i] = listIndex[i];
    }
    (*env).SetFloatArrayRegion(diss,0,k,ds);
    (*env).SetIntArrayRegion(indexs,0,k,ii);
    free(listIndex);
    free(listScore);

    return index->ntotal;

}
extern "C"
JNIEXPORT jlong JNICALL
Java_com_brandservice_utils_IndexUtil_addData(JNIEnv *env, jclass clazz, jfloatArray data_vector) {
    // TODO: implement addData()

    //这里涉及一个jfloatArray和一个float*转换的问题
    if (index == nullptr)

        index = new faiss::IndexFlatL2(FEATURE_COUNT);
    jfloat* inarr  = env->GetFloatArrayElements(data_vector,NULL);

    index->add(1, inarr);
    return (index->ntotal);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_brandservice_utils_IndexUtil_init_1index(JNIEnv *env, jclass clazz) {
    // TODO: implement init_index()
    index = new faiss::IndexFlatL2(FEATURE_COUNT);
}